﻿namespace LetsTiltWPFFirstSample
{
    using System;
    using System.ComponentModel;
    using System.Runtime.CompilerServices;
    using System.Windows;
    using System.Windows.Media.Media3D;

    public class MainViewModel : INotifyPropertyChanged
    {
        public FrameworkElement Element { get; set; }

        private Point cursorCoordinates;
        public Point CursorCoordinates
        {
            get { return this.cursorCoordinates; }
            set
            {
                this.cursorCoordinates = value;
                this.OnPropertyChanged();
                this.OnPropertyChanged("NormalizedPoint");
                this.OnPropertyChanged("XMagnitude");
                this.OnPropertyChanged("YMagnitude");
                this.OnPropertyChanged("XDirection");
                this.OnPropertyChanged("YDirection");
                this.OnPropertyChanged("AngleMagnitude");
                this.OnPropertyChanged("XAngleContribution");
                this.OnPropertyChanged("Angle");
                this.OnPropertyChanged("Depression");
                this.OnPropertyChanged("RotationY");
                this.OnPropertyChanged("RotationX");
                this.OnPropertyChanged("GlobalOffsetZ");
                this.OnPropertyChanged("RotationVector3D");
            }
        }

        private double maxDepression = 3;
        public double MaxDepression
        {
            get { return this.maxDepression; }
            set
            {
                this.maxDepression = value;
                this.OnPropertyChanged();
            }
        }

        private double maxAngle = 0.3;
        public double MaxAngle
        {
            get { return this.maxAngle; }
            set
            {
                this.maxAngle = value;
                this.OnPropertyChanged();
            }
        }

        private int tiltAngle = 3;
        public int TiltAngle
        {
            get { return this.tiltAngle; }
            set
            {
                this.tiltAngle = value;
                this.OnPropertyChanged();
            }
        }

        private bool isZEnabled = true;
        public bool IsZEnabled
        {
            get { return this.isZEnabled; }
            set
            {
                this.isZEnabled = value;
                this.OnPropertyChanged();
            }
        }

        public Vector3D RotationVector3D
        {
            get
            {
                var zRotation = this.IsZEnabled ? this.GlobalOffsetZ : 0.0;
                return new Vector3D(this.RotationX, -this.RotationY, zRotation);
            }
        }

        public double GlobalOffsetZ
        {
            get { return -this.Depression; }
        }

        public double RotationX
        {
            get { return this.Angle * (1 - this.XAngleContribution) * this.YDirection; }
        }

        public double RotationY
        {
            get { return this.Angle * this.XAngleContribution * this.XDirection; }
        }

        public double Depression
        {
            get { return (1 - this.AngleMagnitude) * MaxDepression; }
        }

        public double Angle
        {
            get
            {
                return this.AngleMagnitude * MaxAngle * 180 / Math.PI;
            }
        }

        public double XAngleContribution
        {
            get
            {
                return this.XMagnitude + this.YMagnitude > 0 ? this.XMagnitude / (this.XMagnitude + this.YMagnitude) : 0;
            }
        }

        public double AngleMagnitude
        {
            get
            {
                return this.XMagnitude + this.YMagnitude;
            }
        }

        public Point NormalizedPoint
        {
            get
            {
                return new Point(
                    Math.Min(Math.Max(this.cursorCoordinates.X / (this.ElementCenter.X * 2), 0), 1),
                    Math.Min(Math.Max(this.cursorCoordinates.Y / (this.ElementCenter.Y * 2), 0), 1));
            }
        }

        public Point ElementCenter
        {
            get
            {
                if (this.Element != null)
                {
                    return new Point(this.Element.ActualWidth/2, this.Element.ActualHeight/2);
                }

                return new Point(0, 0);
            }
        }

        public double XMagnitude
        {
            get
            {
                if (double.IsNaN(this.NormalizedPoint.X))
                {
                    return 0.0;
                }

                return Math.Abs(this.NormalizedPoint.X - 0.5);
            }
        }

        public double YMagnitude
        {
            get
            {
                if (double.IsNaN(this.NormalizedPoint.Y))
                {
                    return 0.0;
                }

                return Math.Abs(this.NormalizedPoint.Y - 0.5);
            }
        }

        public double XDirection
        {
            get
            {
                if (double.IsNaN(this.NormalizedPoint.X))
                {
                    return 0.0;
                }

                return -Math.Sign(this.NormalizedPoint.X - 0.5);
            }
        }

        public double YDirection
        {
            get
            {
                if (double.IsNaN(this.NormalizedPoint.Y))
                {
                    return 0.0;
                }

                return Math.Sign(this.NormalizedPoint.Y - 0.5);
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;

        protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            PropertyChangedEventHandler handler = PropertyChanged;
            if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}
